<template>
  <div class="dt-page">
    <h1>防抖 / 节流 专区</h1>

    <section class="card">
      <h2>输入搜索（500ms 防抖）</h2>
      <div class="row">
        <input type="text" placeholder="输入关键字..."
               @input="e => { const v = e.target.value; inputValue = v; debounced.run(v) }" />
        <button @click="debounced.cancel">取消防抖</button>
        <button @click="debounced.flush">立即执行</button>
      </div>
      <p>实时输入：{{ inputValue }}</p>
      <p>防抖结果：{{ debouncedValue }}</p>
    </section>

    <section class="card">
      <h2>按钮点击（1000ms 节流）</h2>
      <div class="row">
        <button @click="() => { raw++; throttled.run() }">节流点击</button>
        <button @click="throttled.cancel">取消节流</button>
      </div>
      <p>尝试点击：{{ raw }} 次，实际处理：{{ hit }} 次</p>
    </section>

    <section class="card">
      <h2>对比：同一连击（600ms）Debounce vs Throttle</h2>
      <div class="row">
        <button :disabled="cmpRunning" @click="runBurst">模拟20次突发事件（每100ms一次）</button>
        <button @click="resetCmp">重置</button>
      </div>
      <div class="compare">
        <div>
          <p>原始触发：<span class="num">{{ cmpRaw }}</span></p>
        </div>
        <div>
          <p>Debounce防抖 命中：<span class="num">{{ cmpDebounced }}</span>
            <span class="badge" :class="{ on: cmpDebPending }">pending</span>
          </p>
          <small>策略：停止触发后等待 600ms 再执行</small>
        </div>
        <div>
          <p>Throttle节流 命中：<span class="num">{{ cmpThrottled }}</span>
            <span class="badge" :class="{ on: cmpThrPending }">pending</span>
          </p>
          <small>策略：每个 600ms 窗口最多执行一次</small>
        </div>
      </div>
    </section>
  </div>
</template>

<script setup>
import { ref } from 'vue'

function useDebounce(fn, wait = 500) {//防抖
  const pending = ref(false)
  let t = null, lastArgs = null
  const run = (...args) => {
    lastArgs = args
    pending.value = true
    clearTimeout(t)
    t = setTimeout(() => { pending.value = false; fn.apply(null, lastArgs) }, wait)
  }
  const cancel = () => { clearTimeout(t); pending.value = false }
  const flush = () => { if (t) { clearTimeout(t); pending.value = false; fn.apply(null, lastArgs) } }
  return { run, cancel, flush, pending }
}

function useThrottle(fn, wait = 1000) {//节流
  const pending = ref(false)
  let t = null, last = 0, lastArgs = null
  const run = (...args) => {
    const now = Date.now(); lastArgs = args
    const remain = wait - (now - last)
    if (remain <= 0) { last = now; pending.value = false; fn.apply(null, lastArgs) }
    else if (!t) { pending.value = true; t = setTimeout(() => { t = null; last = Date.now(); pending.value = false; fn.apply(null, lastArgs) }, remain) }
  }
  const cancel = () => { clearTimeout(t); t = null; pending.value = false }
  return { run, cancel, pending }
}

// 防抖演示
let inputValue = ''
const debouncedValue = ref('')
const debounced = useDebounce((v) => { debouncedValue.value = v }, 500)

// 节流演示
const raw = ref(0)
const hit = ref(0)
const throttled = useThrottle(() => { hit.value++ }, 1000)

// 对比演示
const cmpRaw = ref(0)
const cmpDebounced = ref(0)
const cmpThrottled = ref(0)
const cmpRunning = ref(false)
const cmpDeb = useDebounce(() => { cmpDebounced.value++ }, 600)
const cmpThr = useThrottle(() => { cmpThrottled.value++ }, 600)
const cmpDebPending = cmpDeb.pending
const cmpThrPending = cmpThr.pending

function resetCmp() {
  cmpRaw.value = 0
  cmpDebounced.value = 0
  cmpThrottled.value = 0
  cmpDeb.cancel()
  cmpThr.cancel()
  cmpRunning.value = false
}

async function runBurst() {
  if (cmpRunning.value) return
  resetCmp()
  cmpRunning.value = true
  for (let i = 0; i < 20; i++) {
    cmpRaw.value++
    cmpDeb.run()
    cmpThr.run()
    await new Promise(r => setTimeout(r, 100))
  }
  setTimeout(() => { cmpRunning.value = false }, 800)
}
</script>

<style scoped>
.dt-page { padding: 20px; }
.card { background: var(--demo-bg-color, #fff); border: 1px solid #e4e7ed; border-radius: 8px; padding: 16px; margin-bottom: 16px; }
.row { display: flex; gap: 8px; align-items: center; }
.compare { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 12px; margin-top: 10px; }
.num { font-weight: 700; color: #303133; }
.badge { margin-left: 8px; padding: 2px 6px; border-radius: 10px; background: #f0f2f5; color: #909399; font-size: 12px; }
.badge.on { background: #ecf5ff; color: #409eff; }
input { padding: 8px 12px; border: 1px solid #e4e7ed; border-radius: 4px; }
button { padding: 8px 12px; border: 1px solid #dcdfe6; background: #fff; border-radius: 4px; cursor: pointer; }
button:hover { background: #f5f7fa; }
</style> 